package cn.org.xuanguang.kepler.hormos.rdb.util;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.PropertyUtilsBean;

import java.beans.PropertyDescriptor;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;

/**
 * 基于Apache Commons BeanUtils的属性操作工具类，实现了ObjectPropertyOperator和ObjectConverter接口。
 * 用于操作对象的属性值，以及对象之间的转换。
 * 本类相当于是工具类，配合@see SqlTemplateParser 处理sql占位符的处理，样例如下：
 * <p>
 * SqlTemplateParser parser = new SqlTemplateParser();
 * parser.setTemplate( "SELECT * FROM table WHERE id = #{id}");
 * <p>
 * Map map = Map.of("id", "1");
 * parser.setParameter(map);
 * </p>
 *
 * @since 3.0
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ApacheCommonPropertyOperator implements ObjectPropertyOperator, ObjectConverter {

    // 获取Bean属性的工具实例
    private static final PropertyUtilsBean propertyUtils = BeanUtilsBean.getInstance().getPropertyUtils();

    // 单例模式实例
    public static final ApacheCommonPropertyOperator INSTANCE = new ApacheCommonPropertyOperator();

    /**
     * 获取对象的属性值。
     *
     * @param object 目标对象
     * @param name   属性名称
     * @return 属性值的Optional包装，如果获取失败则返回空的Optional
     */
    @Override
    public Optional<Object> getProperty(Object object, String name) {
        try {
            return Optional.ofNullable(propertyUtils.getProperty(object, name));
        } catch (NoSuchMethodException ignore) {
            // 忽略未找到方法的异常
        } catch (Exception e) {
            ApacheCommonPropertyOperator.log.info("无法获取属性:{},对象:{}", name, object, e);
        }
        return Optional.empty();
    }

    /**
     * 设置对象的属性值。
     *
     * @param object 目标对象
     * @param name   属性名称
     * @param value  属性值
     */
    @Override
    @SneakyThrows
    public void setProperty(Object object, String name, Object value) {
        try {
            BeanUtils.setProperty(object, name, value);
        } catch (Exception err) {
            log.warn(err.getMessage(), err);
        }
    }

    /**
     * 将一个对象转换为指定类型。
     *
     * @param from 源对象
     * @param to   要转换成的目标类型
     * @return 转换后的新对象实例
     */
    @Override
    @SneakyThrows
    public <T> T convert(Object from, Class<T> to) {
        T newInstance = to.getConstructor().newInstance();
        try {
            BeanUtils.copyProperties(newInstance, from);
        } catch (Exception err) {
            log.warn(err.getMessage(), err);
        }
        return newInstance;
    }

    /**
     * 将一个对象转换为指定类型的实例，支持将对象属性映射到Map中。
     *
     * @param from 源对象
     * @param to   要转换成的目标类型的实例供应商
     * @return 转换后的新对象实例
     */
    @Override
    @SneakyThrows
    public <T> T convert(Object from, Supplier<T> to) {
        T instance = to.get();
        try {
            if (instance instanceof Map) {
                @SuppressWarnings("all")
                Map<Object, Object> mapValue = ((Map<Object, Object>) instance);
                // 遍历源对象属性，将其值映射到Map中
                for (PropertyDescriptor propertyDescriptor : BeanUtilsBean.getInstance().getPropertyUtils().getPropertyDescriptors(from)) {
                    mapValue.put(propertyDescriptor.getName(), BeanUtilsBean.getInstance().getPropertyUtils().getProperty(from, propertyDescriptor.getName()));
                }
                return instance;
            }
            BeanUtils.copyProperties(instance, from);
        } catch (Exception err) {
            log.warn(err.getMessage(), err);
        }
        return instance;
    }
}
